#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <assert.h>
#include <cstring>
using namespace std;
#define NUM 1024

#define OUTPUT 1     // 输出重定向
#define ADD_OUTPUT 2 // 追加输出重定向
#define INTPUT 3     // 输入重定向


// check_TRUNC的目标：
// 1. 若是重定向则返回需要重定向的文件名位置
// 2. 并且确定重定向的类型
  int TRUNC_status = -1;
 char* check_TRUNC(char* ptr)
{
  assert(ptr);
  char* start = ptr;
  char* end = ptr + strlen(ptr) - 1;
  
  while (start <= end)
  {
    if (*end == '>')
    {
      if (*(end - 1) == '>')
      {
        // 追加输出重定向
        TRUNC_status = ADD_OUTPUT;
        *(end - 1) = '\0';
          end++;
        break;
      }

      TRUNC_status = OUTPUT;
      *end = '\0';
      end++;
      break;
    }else if (*end == '<')
    {
      TRUNC_status = INTPUT;
      *end = '\0';
      end++;
      break;
    }

    end--;
    if (start > end)
    {
      end = nullptr;
    }
  }

  return end;
}

int main()
{
  // 属于是死循环
  while (1)
  {
    // 首先是打印地址
    cout << "[afeng@_myshell]$ ";
    fflush(stdout); // 解决缓冲区的问题

    // 然后开始接收指令
    char instruct[NUM];
    memset(instruct, '\0', sizeof instruct);
    if (fgets(instruct, sizeof instruct, stdin) == NULL)
    {
      continue;
    }
    instruct[strlen(instruct) - 1] = '\0';

    // 开始拆分出指令
    // 拆分前先判断重定向,从而找出重定向的文件
    int fd;
    char* ptr = nullptr;
    if (ptr = check_TRUNC(instruct))
    {
      // 给出重定向解决方案
      switch(TRUNC_status)
      {
        case ADD_OUTPUT:
          fd = open(ptr, O_WRONLY | O_APPEND | O_CREAT, 0666);
          if (fd < 0)
          {
            perror("pd:");
            exit(-1);
          }
          dup2(fd, 1);
            break;

        case OUTPUT:
          fd = open(ptr, O_WRONLY | O_TRUNC | O_CREAT, 0666);
          if (fd < 0)
          {
            perror("pd:");
            exit(-1);
          }
          dup2(fd, 1);
          break;

        case INTPUT:
          fd = open(ptr, O_RDONLY);
          if (fd < 0)
          {
            perror("pd:");
            exit(-1);
          }
          dup2(fd, 0);
          break;

        default:
          printf("what? bug?");
          break;
      }
    }
  
  
    char* argv[100] = {0};
    argv[0] = strtok(instruct," ");
    int i = 1;
    while (argv[i++] = strtok(NULL, " "));
    //for (int n = 0;argv[n]; n++)
    //  cout << argv[n] << endl;
    
    // 内置命令 1.我们通过子进程替换打印我们需要的结果，父进程不受影响
    // 当需要更改路径时，目标是父进程
    if (strcmp(argv[0],"cd") == 0)
    {
        if (argv[1] != NULL)
          chdir(argv[1]);
        continue;
    }

    pid_t pd = fork();
    if (pd == 0) // child
    {
      execvp(argv[0], argv);
      exit(-1);
    }
    else{
      int status;
      pid_t ret = waitpid(pd, &status, 0);
      if (ret > 0 )
      {
       //  cout << "子进程运行成功，退出码:" << WEXITSTATUS(status)<< endl;
      }else{
       // cout << "子进程运行失败,退出码:" << WEXITSTATUS(status)<< endl;
      }
    }
  }
  return 0;
}
